package com.ryan.ohos.extension.widget.expandablelist;

import ohos.agp.components.AttrSet;
import ohos.agp.components.BaseItemProvider;
import ohos.agp.components.Component;
import ohos.agp.components.ListContainer;
import ohos.app.Context;

public class ExpandableListContainer extends ListContainer {


    public static final int PACKED_POSITION_TYPE_GROUP = 0;


    public static final int PACKED_POSITION_TYPE_CHILD = 1;


    public static final int PACKED_POSITION_TYPE_NULL = 2;


    public static final long PACKED_POSITION_VALUE_NULL = 0x00000000FFFFFFFFL;


    private static final long PACKED_POSITION_MASK_CHILD = 0x00000000FFFFFFFFL;


    private static final long PACKED_POSITION_MASK_GROUP = 0x7FFFFFFF00000000L;


    private static final long PACKED_POSITION_MASK_TYPE = 0x8000000000000000L;


    private static final long PACKED_POSITION_SHIFT_GROUP = 32;


    private static final long PACKED_POSITION_SHIFT_TYPE = 63;


    private static final long PACKED_POSITION_INT_MASK_CHILD = 0xFFFFFFFF;


    private static final long PACKED_POSITION_INT_MASK_GROUP = 0x7FFFFFFF;


    private ExpandableListItemProvider mConnector;


    private ExpandableItemProvider mItemProvider;


    public ExpandableListContainer(Context context) {
        this(context, null);
    }

    public ExpandableListContainer(Context context, AttrSet attrSet) {
        this(context, attrSet, "");
    }

    public ExpandableListContainer(Context context, AttrSet attrSet, String styleName) {
        super(context, attrSet, styleName);
        init();
    }

    private void init() {
        //TODO 这个逻辑应该在executeItemClick 方法中执行，但是没有该方法不执行
        super.setItemClickedListener(new ItemClickedListener() {
            @Override
            public void onItemClicked(ListContainer listContainer, Component component, int i, long l) {
                performItemClick(component, i, l);
            }
        });
    }

    @Override
    public void setItemClickedListener(ItemClickedListener listener) {
        throw new RuntimeException(
                "For ExpandableListView, use setOnChildClickListener or setOnGroupClickListener");
    }

    @Override
    public void setItemProvider(BaseItemProvider itemProvider) {
        throw new RuntimeException(
                "For ExpandableListView, use setItemProvider(BaseItemProvider) instead of " +
                        "setItemProvider(BaseItemProvider)");
    }

    /**
     * setItemProvider
     * @param itemProvider itemProvider
     */
    public void setItemProvider(ExpandableItemProvider itemProvider) {
        mItemProvider = itemProvider;

        if (itemProvider != null) {
            mConnector = new ExpandableListItemProvider(itemProvider);
        } else {
            mConnector = null;
        }

        super.setItemProvider(mConnector);
    }

    /**
     * getExpandableItemProvider
     *
     * @return ExpandableItemProvider
     */
    public ExpandableItemProvider getExpandableItemProvider() {
        return mItemProvider;
    }


    private boolean isHeaderOrFooterPosition(int position) {
        return false;
    }


    private int getFlatPositionForConnector(int flatListPosition) {

        return flatListPosition;
    }


    private int getAbsoluteFlatPosition(int flatListPosition) {
        return flatListPosition;
    }


    private boolean performItemClick(Component v, int position, long id) {
        if (isHeaderOrFooterPosition(position)) {
            return false;
        }

        final int adjustedPosition = getFlatPositionForConnector(position);
        return handleItemClick(v, adjustedPosition, id);
    }


    boolean handleItemClick(Component v, int position, long id) {
        final ExpandableListItemProvider.PositionMetadata posMetadata = mConnector.getUnflattenedPos(position);

        id = getChildOrGroupId(posMetadata.position);

        boolean returnValue;
        if (posMetadata.position.type == ExpandableListContainerPosition.GROUP) {

            if (mOnGroupClickListener != null) {
                if (mOnGroupClickListener.onGroupClick(this, v,
                        posMetadata.position.groupPos, id)) {
                    posMetadata.recycle();
                    return true;
                }
            }

            if (posMetadata.isExpanded()) {
                mConnector.collapseGroup(posMetadata);


                if (mOnGroupCollapseListener != null) {
                    mOnGroupCollapseListener.onGroupCollapse(posMetadata.position.groupPos);
                }
            } else {
                mConnector.expandGroup(posMetadata);


                if (mOnGroupExpandListener != null) {
                    mOnGroupExpandListener.onGroupExpand(posMetadata.position.groupPos);
                }

                final int groupPos = posMetadata.position.groupPos;
                final int groupFlatPos = posMetadata.position.flatListPos;

                //   final int shiftedGroupPosition = groupFlatPos + getHeaderViewsCount();
                final int shiftedGroupPosition = groupFlatPos;
//                smoothScrollToPosition(shiftedGroupPosition + mItemProvider.getChildrenCount(groupPos),
//                        shiftedGroupPosition);
                scrollTo(shiftedGroupPosition);
            }

            returnValue = true;
        } else {
            /* It's a child, so pass on event */
            if (mOnChildClickListener != null) {
                //  playSoundEffect(SoundEffectConstants.CLICK);
                return mOnChildClickListener.onChildClick(this, v, posMetadata.position.groupPos,
                        posMetadata.position.childPos, id);
            }

            returnValue = false;
        }

        posMetadata.recycle();

        return returnValue;
    }

    /**
     * expandGroup
     *
     * @param groupPos groupPos
     * @return boolean
     */
    public boolean expandGroup(int groupPos) {
        return expandGroup(groupPos, false);
    }

    /**
     * expandGroup
     *
     * @param groupPos groupPos
     * @param animate  animate
     * @return boolean
     */
    public boolean expandGroup(int groupPos, boolean animate) {
        ExpandableListContainerPosition elGroupPos = ExpandableListContainerPosition.obtain(
                ExpandableListContainerPosition.GROUP, groupPos, -1, -1);
        ExpandableListItemProvider.PositionMetadata pm = mConnector.getFlattenedPos(elGroupPos);
        elGroupPos.recycle();
        boolean retValue = mConnector.expandGroup(pm);

        if (mOnGroupExpandListener != null) {
            mOnGroupExpandListener.onGroupExpand(groupPos);
        }

        if (animate) {
            final int groupFlatPos = pm.position.flatListPos;

            // final int shiftedGroupPosition = groupFlatPos + getHeaderViewsCount();
            final int shiftedGroupPosition = groupFlatPos;
//            smoothScrollToPosition(shiftedGroupPosition + mItemProvider.getChildrenCount(groupPos),
//                    shiftedGroupPosition);
            scrollTo(shiftedGroupPosition + mItemProvider.getChildrenCount(groupPos));
        }
        pm.recycle();

        return retValue;
    }

    /**
     * collapseGroup
     *
     * @param groupPos groupPos
     * @return boolean
     */
    public boolean collapseGroup(int groupPos) {
        boolean retValue = mConnector.collapseGroup(groupPos);

        if (mOnGroupCollapseListener != null) {
            mOnGroupCollapseListener.onGroupCollapse(groupPos);
        }

        return retValue;
    }

    /**
     * OnGroupCollapseListener
     */
    public interface OnGroupCollapseListener {
        /**
         * onGroupCollapse
         *
         * @param groupPosition groupPosition
         */
        void onGroupCollapse(int groupPosition);
    }

    private OnGroupCollapseListener mOnGroupCollapseListener;

    /**
     * setOnGroupCollapseListener
     *
     * @param onGroupCollapseListener onGroupCollapseListener
     */
    public void setOnGroupCollapseListener(
            OnGroupCollapseListener onGroupCollapseListener) {
        mOnGroupCollapseListener = onGroupCollapseListener;
    }

    /**
     * OnGroupExpandListener
     */
    public interface OnGroupExpandListener {
        /**
         * onGroupExpand
         *
         * @param groupPosition groupPosition
         */
        void onGroupExpand(int groupPosition);
    }

    private OnGroupExpandListener mOnGroupExpandListener;

    /**
     * setOnGroupExpandListener
     *
     * @param onGroupExpandListener onGroupExpandListener
     */
    public void setOnGroupExpandListener(
            OnGroupExpandListener onGroupExpandListener) {
        mOnGroupExpandListener = onGroupExpandListener;
    }

    /**
     * OnGroupClickListener
     */
    public interface OnGroupClickListener {
        /**
         * onGroupClick
         *
         * @param parent        parent
         * @param v             v
         * @param groupPosition groupPosition
         * @param id            id
         * @return boolean
         */
        boolean onGroupClick(ExpandableListContainer parent, Component v, int groupPosition,
                             long id);
    }

    private OnGroupClickListener mOnGroupClickListener;

    /**
     * setOnGroupClickListener
     *
     * @param onGroupClickListener onGroupClickListener
     */
    public void setOnGroupClickListener(OnGroupClickListener onGroupClickListener) {
        mOnGroupClickListener = onGroupClickListener;
    }

    /**
     * OnChildClickListener
     */
    public interface OnChildClickListener {
        /**
         * onChildClick
         *
         * @param parent        parent
         * @param v             v
         * @param groupPosition groupPosition
         * @param childPosition childPosition
         * @param id            id
         * @return boolean
         */
        boolean onChildClick(ExpandableListContainer parent, Component v, int groupPosition,
                             int childPosition, long id);
    }

    private OnChildClickListener mOnChildClickListener;

    /**
     * setOnChildClickListener
     *
     * @param onChildClickListener onChildClickListener
     */
    public void setOnChildClickListener(OnChildClickListener onChildClickListener) {
        mOnChildClickListener = onChildClickListener;
    }

    /**
     * getExpandableListPosition
     *
     * @param flatListPosition flatListPosition
     * @return long
     */
    public long getExpandableListPosition(int flatListPosition) {
        if (isHeaderOrFooterPosition(flatListPosition)) {
            return PACKED_POSITION_VALUE_NULL;
        }

        final int adjustedPosition = getFlatPositionForConnector(flatListPosition);
        ExpandableListItemProvider.PositionMetadata pm = mConnector.getUnflattenedPos(adjustedPosition);
        long packedPos = pm.position.getPackedPosition();
        pm.recycle();
        return packedPos;
    }

    /**
     * getFlatListPosition
     *
     * @param packedPosition packedPosition
     * @return int
     */
    public int getFlatListPosition(long packedPosition) {
        ExpandableListContainerPosition elPackedPos = ExpandableListContainerPosition
                .obtainPosition(packedPosition);
        ExpandableListItemProvider.PositionMetadata pm = mConnector.getFlattenedPos(elPackedPos);
        elPackedPos.recycle();
        final int flatListPosition = pm.position.flatListPos;
        pm.recycle();
        return getAbsoluteFlatPosition(flatListPosition);
    }

    /**
     * getSelectedPosition
     *
     * @return long
     */
    public long getSelectedPosition() {
        final int selectedPos = getSelectedItemIndex();

        // The case where there is no selection (selectedPos == -1) is also handled here.
        return getExpandableListPosition(selectedPos);
    }

    /**
     * getSelectedId
     *
     * @return long
     */
    public long getSelectedId() {
        long packedPos = getSelectedPosition();
        if (packedPos == PACKED_POSITION_VALUE_NULL) return -1;

        int groupPos = getPackedPositionGroup(packedPos);

        if (getPackedPositionType(packedPos) == PACKED_POSITION_TYPE_GROUP) {
            // It's a group
            return mItemProvider.getGroupId(groupPos);
        } else {
            // It's a child
            return mItemProvider.getChildId(groupPos, getPackedPositionChild(packedPos));
        }
    }

    /**
     * groupPosition
     *
     * @param groupPosition groupPosition
     * @return boolean
     */
    public boolean isGroupExpanded(int groupPosition) {
        return mConnector.isGroupExpanded(groupPosition);
    }

    /**
     * getPackedPositionType
     *
     * @param packedPosition packedPosition
     * @return int
     */
    public static int getPackedPositionType(long packedPosition) {
        if (packedPosition == PACKED_POSITION_VALUE_NULL) {
            return PACKED_POSITION_TYPE_NULL;
        }

        return (packedPosition & PACKED_POSITION_MASK_TYPE) == PACKED_POSITION_MASK_TYPE
                ? PACKED_POSITION_TYPE_CHILD
                : PACKED_POSITION_TYPE_GROUP;
    }

    /**
     * getPackedPositionGroup
     *
     * @param packedPosition packedPosition
     * @return int
     */
    public static int getPackedPositionGroup(long packedPosition) {
        // Null
        if (packedPosition == PACKED_POSITION_VALUE_NULL) return -1;

        return (int) ((packedPosition & PACKED_POSITION_MASK_GROUP) >> PACKED_POSITION_SHIFT_GROUP);
    }

    /**
     * getPackedPositionChild
     *
     * @param packedPosition packedPosition
     * @return int
     */
    public static int getPackedPositionChild(long packedPosition) {
        // Null
        if (packedPosition == PACKED_POSITION_VALUE_NULL) return -1;

        // Group since a group type clears this bit
        if ((packedPosition & PACKED_POSITION_MASK_TYPE) != PACKED_POSITION_MASK_TYPE) return -1;

        return (int) (packedPosition & PACKED_POSITION_MASK_CHILD);
    }

    /**
     * getPackedPositionForChild
     *
     * @param groupPosition groupPosition
     * @param childPosition childPosition
     * @return long
     */
    public static long getPackedPositionForChild(int groupPosition, int childPosition) {
        return (((long) PACKED_POSITION_TYPE_CHILD) << PACKED_POSITION_SHIFT_TYPE)
                | ((((long) groupPosition) & PACKED_POSITION_INT_MASK_GROUP)
                << PACKED_POSITION_SHIFT_GROUP)
                | (childPosition & PACKED_POSITION_INT_MASK_CHILD);
    }

    /**
     * getPackedPositionForGroup
     *
     * @param groupPosition
     * @return long
     */
    public static long getPackedPositionForGroup(int groupPosition) {
        // No need to OR a type in because PACKED_POSITION_GROUP == 0
        return ((((long) groupPosition) & PACKED_POSITION_INT_MASK_GROUP)
                << PACKED_POSITION_SHIFT_GROUP);
    }


    private long getChildOrGroupId(ExpandableListContainerPosition position) {
        if (position.type == ExpandableListContainerPosition.CHILD) {
            return mItemProvider.getChildId(position.groupPos, position.childPos);
        } else {
            return mItemProvider.getGroupId(position.groupPos);
        }
    }
}
